﻿using AnimCollections;
using AOT;
using System;
using Unity.Burst;
using UnityEngine.Animations;
using UnityEngine.Playables;

namespace DA.AnimGraph {
    [BurstCompile]
    public unsafe partial struct RevolveNode : INode {
        public struct SetupData : INodeSetupData {
            public IntLinkDesc listenDesc;
            public float defaultTransitionDuration;
        }

        //
        private IntLink linkListen;
        private float defaultTransitionDuration;
        private TransitionEvaluator transitionEvaluator;

        //
        private NodeHead head;
        private AnimationMixerPlayable playable;
        private int lastListen;

        [BurstCompile]
        [MonoPInvokeCallback(typeof(NodeLink.DisposeDelegate))]
        private static bool Dispose(in void* node) {
            var self = (RevolveNode*)node;

            self->transitionEvaluator.Dispose();
            return true;
        }

        private void Setup(in SetupData data, ref GraphBuffer buffer, in NodeLink* link) {
            linkListen = new IntLink(data.listenDesc);
            defaultTransitionDuration = data.defaultTransitionDuration;
            transitionEvaluator = new TransitionEvaluator(2, ref buffer);
        }
        private static Playable BuildPlayableGraph(in void* node, in NodeLink* link, in PlayableGraph graph, in BuildContext buildContext) {
            var self = (RevolveNode*)node;

            self->head = new NodeHead(link);

            var childrenCount = self->head.childrenCount;
            self->playable = AnimationMixerPlayable.Create(graph, childrenCount);
            for (int i = 0; i < childrenCount; i++) {
                self->playable.ConnectInput(i, self->head.children[i]->BuildPlayableGraph(graph, buildContext), 0);
            }

            return self->playable;
        }

        [BurstCompile]
        [MonoPInvokeCallback(typeof(NodeLink.EvaluateDelegate))]
        private static void Evaluate(in void* node, bool onEnter, in EvaluateContext evaluateContext, in GraphContext graphContext, ref UpstreamData upstream, ref ScopeContext scope) {
            var self = (RevolveNode*)node;

            bool addSuccess = false;
            if (onEnter) {
                self->transitionEvaluator.SetFirstState(0);
                self->lastListen = self->linkListen.Evaluate(evaluateContext, graphContext, scope);
            } else {
                var curListen = self->linkListen.Evaluate(evaluateContext, graphContext, scope);
                if (self->lastListen != curListen) {
                    self->lastListen = curListen;
                    var childrenCount = self->head.childrenCount;
                    if (childrenCount == 1) {
                        addSuccess = true;
                    } else {
                        int newState = self->transitionEvaluator.newestState + 1;
                        newState = newState >= childrenCount ? 0 : newState;
                        addSuccess = self->transitionEvaluator.AddNewState(newState, self->defaultTransitionDuration, false);
                    }
                }
            }

            float deltaTime = evaluateContext.deltaTime;
            self->transitionEvaluator.Evaluate(deltaTime);
            self->transitionEvaluator.GetEnumerator(out TransitionEvaluator.Iter iter);
            while (iter.MoveNext()) {
                var (port, weight) = iter.current;
                var childScope = new ScopeContext(scope, weight);
                childScope.forceOnEnter = iter.CurrentIsNewestState() && addSuccess; // 主要针对只有一个子节点的情况，确保能当作新进入处理
                self->head.weights[port] = weight;
                NodeLink.Evaluate(self->head.children[port], evaluateContext, graphContext, ref upstream, ref childScope);
                scope.Fusion(childScope);
            }
        }
        [BurstCompile]
        [MonoPInvokeCallback(typeof(NodeLink.GetTimeStateDelegate))]
        private static IntPtr GetTimeState(in void* node) {
            var self = (RevolveNode*)node;
            // 返回当前决定的状态，无论它权重多少
            int newestState = self->transitionEvaluator.newestState;
            var result = self->head.children[newestState]->GetTimeState();
            return (IntPtr)result;
        }
    }
}
